package com.imis.module.api.bus;

import cn.hutool.core.util.StrUtil;
import com.imis.base.annotation.VerificationCaptcha;
import com.imis.base.constant.CacheConstant;
import com.imis.base.constant.CommonConstant;
import com.imis.base.constant.enums.SysTipEnum;
import com.imis.base.constant.enums.VerificationCodeTypeEnum;
import com.imis.base.globle.Result;
import com.imis.base.shiro.util.JwtUtil;
import com.imis.base.constant.HttpHeadersConstants;
import com.imis.base.util.ConvertUtils;
import com.imis.base.util.CurrentUserUtils;
import com.imis.base.util.PasswordUtil;
import com.imis.module.base.BaseBus;
import com.imis.module.system.model.converter.SysUserConverter;
import com.imis.module.system.model.po.SysUser;
import com.imis.module.api.model.ro.SysLoginRO;
import com.imis.module.api.model.ro.SysPhoneLoginRO;
import com.imis.module.api.model.ro.SysRegisterRO;
import com.imis.module.api.model.ro.SysUserPasswordChangeRO;
import com.imis.module.system.model.vo.SysMenuTreeVO;
import com.imis.module.system.model.vo.SysUserVO;
import com.imis.module.system.service.ISysMenuService;
import com.imis.module.system.service.ISysRoleService;
import com.imis.module.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * SysLoginBus<br>
 * 登录相关功能业务处理类
 * </p>
 *
 * @author XinLau
 * @version 1.0
 * @since 2020年03月10日 09:18
 */
@Service
public class SysLoginBus extends BaseBus {

    /**
     * 系统用户表 服务类
     */
    private ISysUserService sysUserService;

    @Autowired
    public void setSysUserService(ISysUserService sysUserService) {
        this.sysUserService = sysUserService;
    }

    /**
     * 系统角色表-菜单权限组 服务类
     */
    private ISysRoleService serviceBySysRoleService;

    @Autowired
    public void setServiceBySysRoleService(ISysRoleService serviceBySysRoleService) {
        this.serviceBySysRoleService = serviceBySysRoleService;
    }

    /**
     * 功能菜单表 服务类
     */
    private ISysMenuService serviceBySysMenuService;

    @Autowired
    public void setServiceBySysMenuService(ISysMenuService serviceBySysMenuService) {
        this.serviceBySysMenuService = serviceBySysMenuService;
    }

    /**
     * 账号登录校验用户是否有效
     *
     * @param sysUser - 系统用户
     * @return Result
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/3/9 9:47
     */
    private Result checkUserIsEffective(SysUser sysUser) {
        Result<SysUserVO> result = new Result<>();
//        情况1：根据用户信息查询，该用户不存在
        if (ConvertUtils.isEmpty(sysUser)) {
            addLog(CommonConstant.LOG_TYPE_LOGIN, CommonConstant.OPERATE_TYPE_QUERY, SysTipEnum.USER_LOGIN_ERR_NON.toString());
            return result.errorFormat(SysTipEnum.USER_LOGIN_ERR_NON);
        }
//        情况2：根据用户信息查询，该用户已注销
        if (CommonConstant.DEL_FLAG_DELETE.equals(sysUser.getDelFlag())) {
            addLog(CommonConstant.LOG_TYPE_LOGIN, CommonConstant.OPERATE_TYPE_QUERY, SysTipEnum.USER_LOGIN_ERR_DELETE.toString(), sysUser.getUsername());
            return result.errorFormat(SysTipEnum.USER_LOGIN_ERR_DELETE, sysUser.getUsername());
        }
//        情况3：根据用户信息查询，该用户已冻结
        if (CommonConstant.USER_FREEZE.equals(sysUser.getStatus())) {
            addLog(CommonConstant.LOG_TYPE_LOGIN, CommonConstant.OPERATE_TYPE_QUERY, SysTipEnum.USER_LOGIN_ERR_FREEZE.toString(), sysUser.getUsername());
            return result.errorFormat(SysTipEnum.USER_LOGIN_ERR_FREEZE, sysUser.getUsername());
        }
        return result;
    }

    /**
     * 手机号登录校验用户是否有效
     *
     * @param sysUser - 系统用户
     * @return Result
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/3/9 9:47
     */
    private Result checkPhoneIsEffective(SysUser sysUser) {
        Result result = new Result<>();
//        情况1：根据用户信息查询，该用户不存在
        if (ConvertUtils.isEmpty(sysUser)) {
            addLog(CommonConstant.LOG_TYPE_LOGIN, CommonConstant.OPERATE_TYPE_QUERY, SysTipEnum.USER_LOGIN_ERR_NON.toString());
            return result.errorFormat(SysTipEnum.USER_LOGIN_ERR_NON);
        }
//        情况2：根据用户信息查询，该用户已注销
        if (CommonConstant.DEL_FLAG_DELETE.equals(sysUser.getDelFlag())) {
            addLog(CommonConstant.LOG_TYPE_LOGIN, CommonConstant.OPERATE_TYPE_QUERY, SysTipEnum.USER_LOGIN_ERR_DELETE.toString(), sysUser.getPhone());
            return result.errorFormat(SysTipEnum.USER_LOGIN_ERR_DELETE, sysUser.getPhone());
        }
//        情况3：根据用户信息查询，该用户已冻结
        if (CommonConstant.USER_FREEZE.equals(sysUser.getStatus())) {
            addLog(CommonConstant.LOG_TYPE_LOGIN, CommonConstant.OPERATE_TYPE_QUERY, SysTipEnum.USER_LOGIN_ERR_FREEZE.toString(), sysUser.getPhone());
            return result.errorFormat(SysTipEnum.USER_LOGIN_ERR_FREEZE, sysUser.getPhone());
        }
        return result;
    }

    /**
     * 注册校验用户
     *
     * @param sysUserList - 系统用户
     * @return Result
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/3/9 9:47
     */
    private Result registrationVerification(List<SysUser> sysUserList) {
        if (sysUserList.size() > 0) {
            for (SysUser sysUser : sysUserList) {
//                情况1：根据用户信息查询，该用户已注销
                if (CommonConstant.DEL_FLAG_DELETE.equals(sysUser.getDelFlag())) {
                    addLog(CommonConstant.LOG_TYPE_REGISTER, CommonConstant.OPERATE_TYPE_ADD, SysTipEnum.USER_REGISTER_ERR_DELETE.toString(), sysUser.getUsername());
                    return Result.errorsFormat(SysTipEnum.USER_REGISTER_ERR_DELETE, sysUser.getUsername());
                }
//                情况2.根据用户信息查询，该用户已冻结
                if (CommonConstant.USER_FREEZE.equals(sysUser.getStatus())) {
                    addLog(CommonConstant.LOG_TYPE_REGISTER, CommonConstant.OPERATE_TYPE_ADD, SysTipEnum.USER_REGISTER_ERR_FREEZE.toString(), sysUser.getUsername());
                    return Result.errorsFormat(SysTipEnum.USER_REGISTER_ERR_FREEZE, sysUser.getUsername());
                }
//                情况3.根据用户信息查询，该用户存在
                addLog(CommonConstant.LOG_TYPE_REGISTER, CommonConstant.OPERATE_TYPE_ADD, SysTipEnum.USER_REGISTER_ERR_REPEAT.toString(), sysUser.getUsername());
                return Result.errorsFormat(SysTipEnum.USER_REGISTER_ERR_REPEAT, sysUser.getUsername());
            }
        }
        return Result.ok();
    }

    /**
     * 用户相关信息
     *
     * @param sysUser 系统用户
     * @param result  - 处理结果
     * @return Result
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/3/10 10:30
     */
    private Result userInfo(SysUser sysUser, Result<SysUserVO> result) {
        /**
         * Tip：修改此处Token需要连带修改
         * com.imis.base.shiro.authc.ShiroRealm#jwtTokenRefresh(java.lang.String, java.lang.String, java.lang.String)
         * com.imis.module.system.bus.SysUserBus#freezeUserByIdentification(java.lang.String)
         * com.imis.module.api.bus.SysLoginBus#logout(javax.servlet.http.HttpServletRequest)
         * com.imis.base.shiro.authc.ShiroRealm#jwtTokenRefresh(java.lang.String, java.lang.String, java.lang.String)
         */
        String sysPassword = sysUser.getPassword();
        String username = sysUser.getUsername();
//        生成Token
        String token = JwtUtil.sign(username, sysPassword);
//        生成Key
        String key = JwtUtil.getTokenKey(username, sysPassword);
//        1.存放用户Token     设置超时时间
        redisUtil.set(key, token, JwtUtil.EXPIRE_TIME);
//        用户信息返回值
        SysUserVO sysUserVO = SysUserConverter.INSTANCE.domain2vo(sysUser);
//        返回生成的Token
        sysUserVO.setToken(token);
        result.setResult(sysUserVO);
//        2.存放用户编号与Token的Key
        Map<String, Object> userTokenMap = new HashMap<>(1);
        userTokenMap.put(sysUser.getId().toString(), key);
        redisUtil.hmset(CacheConstant.PREFIX_SYS_USER_ID_TOKEN, userTokenMap);
//        3.添加日志
        addLog(CommonConstant.LOG_TYPE_LOGIN, CommonConstant.OPERATE_TYPE_QUERY, SysTipEnum.USER_LOGIN_OK.toString(), username);
        return result.success();
    }

    /**
     * 用户登录
     *
     * @param sysLoginRO - 用户登录表单对象
     * @return Result
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/3/6 14:26
     */
    @VerificationCaptcha(verification = false, type = VerificationCodeTypeEnum.OPERATION, captcha = "#sysLoginRO.captcha", verificationCodeIdentification = "#sysLoginRO.verificationCodeIdentification")
    public Result login(final SysLoginRO sysLoginRO) {
        String username = sysLoginRO.getUsername();
//        1. 校验用户是否有效
        if (ConvertUtils.isEmpty(username) || ConvertUtils.isEmpty(sysLoginRO.getPassword())) {
            return Result.errorsFormat(SysTipEnum.USER_LOGIN_ERR_NON);
        }
        SysUser sysUser = sysUserService.queryUserByName(username);
        Result result = checkUserIsEffective(sysUser);
        if (!result.isSuccess()) {
            return result;
        }
//        2. 校验用户名或密码是否正确
        String userPassword = PasswordUtil.encrypt(username, sysLoginRO.getPassword(), sysUser.getSalt());
        if (!sysUser.getPassword().equals(userPassword)) {
            addLog(CommonConstant.LOG_TYPE_LOGIN, CommonConstant.OPERATE_TYPE_QUERY, SysTipEnum.USER_LOGIN_ERR.toString(), username);
            return Result.errorsFormat(SysTipEnum.USER_LOGIN_ERR, username);
        }
//        3.用户登录信息
        return userInfo(sysUser, result);
    }

    /**
     * 用户登录
     *
     * @param sysPhoneLoginRO - 手机登录表单对象
     * @return Result
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/3/6 14:26
     */
    @VerificationCaptcha(type = VerificationCodeTypeEnum.SMS, captcha = "#sysPhoneLoginRO.captcha", verificationCodeIdentification = "#sysPhoneLoginRO.phone")
    public Result loginByPhone(final SysPhoneLoginRO sysPhoneLoginRO) {
//        1.校验用户是否有效
        String loginPhone = sysPhoneLoginRO.getPhone();
        if (ConvertUtils.isEmpty(loginPhone)) {
            return Result.errorsFormat(SysTipEnum.USER_LOGIN_ERR_PHONE_NON);
        }
        SysUser sysUser = sysUserService.queryUserByPhone(loginPhone);
        Result result = checkPhoneIsEffective(sysUser);
        if (!result.isSuccess()) {
            return result;
        }
//        2.用户登录信息
        return userInfo(sysUser, result);
    }

    /**
     * 用户注册
     *
     * @param sysRegisterRO - 用户注册对象
     * @return Result
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/3/5 17:25
     */
    @VerificationCaptcha(type = VerificationCodeTypeEnum.SMS, captcha = "#sysRegisterRO.captcha", verificationCodeIdentification = "#sysRegisterRO.verificationCodeIdentification")
    public Result register(final SysRegisterRO sysRegisterRO) {
//        1、校验用户重复
        List<SysUser> sysUserList = sysUserService.duplicateRegistrationVerification(sysRegisterRO);
        Result result = registrationVerification(sysUserList);
        if (!result.isSuccess()) {
            return result;
        }
//        2、创建新用户
        SysUser sysUser = SysUserConverter.INSTANCE.ro2Domain(sysRegisterRO);
        sysUser.setCreateBy(0L);
        if (sysUserService.saveUser(sysUser)) {
//            成功
            addLog(CommonConstant.LOG_TYPE_REGISTER, CommonConstant.OPERATE_TYPE_ADD, SysTipEnum.USER_REGISTER_OK.toString(), sysUser.getUsername());
            result.setResult(SysUserConverter.INSTANCE.domain2vo(sysUser));
            return result.success();
        }
//        失败
        addLog(CommonConstant.LOG_TYPE_REGISTER, CommonConstant.OPERATE_TYPE_ADD, SysTipEnum.USER_REGISTER_ERR.toString(), sysRegisterRO.getUsername());
        return result.errorFormat(SysTipEnum.USER_REGISTER_ERR);
    }

    /**
     * 用户登出
     *
     * @param request - 请求
     * @return Result
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/3/10 10:20
     */
    public Result logout(HttpServletRequest request) {
        try {
//            1.获取前端Token
            String requestToken = request.getHeader(HttpHeadersConstants.X_ACCESS_TOKEN);
            if (StrUtil.isEmpty(requestToken)) {
                addLog(CommonConstant.LOG_TYPE_LOGOUT, null, SysTipEnum.USER_LOGOUT_ERR_REQUEST_TOKEN_NULL.toString());
                return Result.errorsFormat(SysTipEnum.USER_LOGOUT_ERR_REQUEST_TOKEN_NULL);
            }
//            Token的Key
//            String tokenKey = CacheConstant.PREFIX_SYS_USER_TOKEN + requestToken;
            String tokenKey = (String) redisUtil.hget(CacheConstant.PREFIX_SYS_USER_ID_TOKEN, CurrentUserUtils.getLoginUserId().toString());
//            2.获取用户Token的Redis缓存
            String redisToken = (String) redisUtil.get(tokenKey);
            if (StrUtil.isEmpty(redisToken)) {
                addLog(CommonConstant.LOG_TYPE_LOGOUT, null, SysTipEnum.USER_LOGOUT_ERR_REDIS_TOKEN_NULL.toString());
                return Result.errorsFormat(SysTipEnum.USER_LOGOUT_ERR_REDIS_TOKEN_NULL);
            }
//            3.前后端缓存比对
            if (!redisToken.equals(requestToken)) {
                addLog(CommonConstant.LOG_TYPE_LOGOUT, null, SysTipEnum.USER_LOGOUT_ERR_REQUEST_TOKEN_REDIS_TOKEN_DIFFERENT.toString());
                return Result.errorsFormat(SysTipEnum.USER_LOGOUT_ERR_REQUEST_TOKEN_REDIS_TOKEN_DIFFERENT);
            }
//            4.删除 用户Token缓存
            redisUtil.del(tokenKey);
//            5.退出登录
            CurrentUserUtils.getSubject().logout();
        } catch (Exception e) {
            addLog(CommonConstant.LOG_TYPE_LOGOUT, null, SysTipEnum.USER_LOGOUT_ERR.toString(), e.getMessage());
            return Result.errorsFormat(SysTipEnum.USER_LOGOUT_ERR, e.getMessage());
        }
        addLog(CommonConstant.LOG_TYPE_LOGOUT, null, SysTipEnum.USER_LOGOUT_OK.toString());
        return Result.ok();
    }

    /**
     * 查询当前用户的功能菜单树
     *
     * @return List
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/3/23 12:31
     */
    public Result querySysMenuTreeListByCurrentUser() {
//        获取当前用户
        SysUserVO sysUserVO = CurrentUserUtils.getLoginUser();
//        1.用户合法性判断
        if (ConvertUtils.isEmpty(sysUserVO)) {
            addLog(CommonConstant.LOG_TYPE_OPERATE, CommonConstant.OPERATE_TYPE_QUERY, SysTipEnum.MENU_TREE_QUERY_ERR_NON_USER.toString());
            return Result.errorsFormat(SysTipEnum.MENU_TREE_QUERY_ERR_NON_USER);
        }
//        获取用户角色
        List<Long> roleIdListByUserId = serviceBySysRoleService.selectSysRoleIdArrayByUserId(CurrentUserUtils.getLoginUserId());
//        2.用户拥有角色判断
        if (roleIdListByUserId.size() <= 0) {
            addLog(CommonConstant.LOG_TYPE_OPERATE, CommonConstant.OPERATE_TYPE_QUERY, SysTipEnum.MENU_TREE_QUERY_ERR_NON_ROLE.toString(), sysUserVO.getUsername());
            return Result.errorsFormat(SysTipEnum.MENU_TREE_QUERY_ERR_NON_ROLE, sysUserVO.getUsername());
        }
        try {
//            获取所有角色的功能菜单树
            List<SysMenuTreeVO> sysMenuTreeListByRoleId = serviceBySysMenuService.querySysMenuTreeListByRoleIdList(Long.valueOf(sysUserVO.getId()), roleIdListByUserId);
            return Result.ok(sysMenuTreeListByRoleId);
        } catch (Exception e) {
            addLog(CommonConstant.LOG_TYPE_OPERATE, CommonConstant.OPERATE_TYPE_QUERY, SysTipEnum.MENU_TREE_QUERY_ERR.toString(), e.getMessage());
            return Result.errorsFormat(SysTipEnum.MENU_TREE_QUERY_ERR, e.getMessage());
        }
    }

    /**
     * 用户自助修改密码
     *
     * @param sysUserPasswordChangeRO - 自助密码修改对象
     * @return Result
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/3/23 12:31
     */
    @VerificationCaptcha(type = VerificationCodeTypeEnum.SMS, captcha = "#sysUserPasswordChangeRO.captcha", verificationCodeIdentification = "#sysUserPasswordChangeRO.verificationCodeIdentification")
    public Result passwordChange(final SysUserPasswordChangeRO sysUserPasswordChangeRO) {
//        1.密码重复性判断
        String username = sysUserPasswordChangeRO.getUsername();
        if (ConvertUtils.isEmpty(sysUserPasswordChangeRO.getNewPassword())) {
            addLog(CommonConstant.LOG_TYPE_OPERATE, CommonConstant.OPERATE_TYPE_UPDATE, SysTipEnum.CHANGE_USER_PASSWORD_ERR_NON_PASSWORD.toString());
            return Result.errorsFormat(SysTipEnum.CHANGE_USER_PASSWORD_ERR_NON_PASSWORD);
        }
//        2.用户合法性判断
        SysUser sysUser = sysUserService.queryUserByName(username);
        if (ConvertUtils.isEmpty(sysUser)) {
            addLog(CommonConstant.LOG_TYPE_OPERATE, CommonConstant.OPERATE_TYPE_UPDATE, SysTipEnum.CHANGE_USER_PASSWORD_ERR_NON_USER.toString(), username);
            return Result.errorsFormat(SysTipEnum.CHANGE_USER_PASSWORD_ERR_NON_USER, username);
        }
//        3.修改密码
        /**
         * 这里不考虑清缓存：业务场景：用户重置密码，用户不在线
         */
        boolean changeUserPassword = sysUserService.changeUserPassword(sysUser.getUsername(), sysUserPasswordChangeRO.getNewPassword());
        if (changeUserPassword) {
            addLog(CommonConstant.LOG_TYPE_OPERATE, CommonConstant.OPERATE_TYPE_UPDATE, SysTipEnum.CHANGE_USER_PASSWORD_OK.toString(), sysUser.getUsername());
            return Result.ok();
        }
        addLog(CommonConstant.LOG_TYPE_OPERATE, CommonConstant.OPERATE_TYPE_UPDATE, SysTipEnum.CHANGE_USER_PASSWORD_ERR.toString(), sysUser.getUsername());
        return Result.errorsFormat(SysTipEnum.TERMINATE_ALL_ROLE_TO_USER_ERR, sysUser.getUsername());
    }

}
